package com.hup.sokoban.view.customView;

import com.hup.sokoban.model.common.StepRecord;
import com.hup.sokoban.model.common.StepRecord.CellInfo;
import com.hup.sokoban.service.common.AudioService;
import com.hup.sokoban.service.common.AutoplayWinningService;
import com.hup.sokoban.service.common.AutoplayWinningService.AutoplayObserver;
import com.hup.utils.commons.start.SpringContext;
import lombok.Setter;
import lombok.extern.log4j.Log4j2;

import javax.swing.*;
import java.awt.*;
import java.util.ArrayList;

/**
 * 步数记录面板:[记录操作,撤回功能,自动播放]
 * @author hugan
 * @date 2020/5/4
 */
@Log4j2
public class StepRecordPanel extends JPanel implements AutoplayObserver {

    private final AudioService audioService;
    private JCheckBox cbAutoplay;
    private JSlider slider;
    private JLabel labStep;

    private final ArrayList<StepRecord> lstStep = new ArrayList<>();
    @Setter
    private MapLabel mapLabel;

    /**
     * 代码设置slider
     */
    private boolean notUpdateView;
    private int max = 0;
    private int cur = 0;

    @Setter
    private AutoplayWinningService autoplayWinningService;

    public StepRecordPanel() {
        super(new BorderLayout());
        audioService = SpringContext.getBean(AudioService.class);
        initView();
        initListener();
    }

    private void initView() {
        cbAutoplay = new JCheckBox("自动播放");
        cbAutoplay.setVisible(false);
        add(cbAutoplay, BorderLayout.WEST);

        slider = new JSlider(0, max, cur);
        slider.setFocusable(false);
        slider.setPaintTicks(true);
        slider.setMinorTickSpacing(1);
        slider.setMajorTickSpacing(20);//刻度
        add(slider);

        labStep = new JLabel("[0/0]");
        add(labStep, BorderLayout.EAST);
    }

    private void initListener() {
        cbAutoplay.addActionListener(e -> autoplayWinningService.setPlayState(cbAutoplay.isSelected()));
        slider.addChangeListener(e -> {
            boolean isAdjusting = slider.getValueIsAdjusting();
            log.debug("isAdjusting={}", isAdjusting);
            if (isAdjusting && autoplayWinningService != null) {
                //用户拖拽
                autoplayWinningService.setPlayState(false);
            }
            int cur = slider.getValue();
            if (!notUpdateView && this.cur != cur) {
                log.debug("拖拽/撤销/恢复={},e={}", cur, e);
                int old = this.cur;
                this.cur = cur;
                labStep.setText("[" + this.cur + "/" + max + "]");
                updateView(old, this.cur);
                if (isAdjusting) {
                    //这里的代码结构不太好
                    log.debug("用户拖拽");
                    audioService.move();
                }
            }
        });
    }

    /**
     * 更新[text和MapLabel]
     */
    private void updateView(int oldVal, int newVal) {
        log.debug("oldVal={} -> newVal={}", oldVal, newVal);
        int[] map = mapLabel.getMapItem().getMap();

        if (oldVal > newVal) {
            //for (int i = 1; i > 0; i--) {
            for (int i = oldVal; i > newVal; i--) {
                log.debug("撤销,i={}", i);
                StepRecord record = lstStep.get(i - 1);
                for (CellInfo cell : record.getCells()) {
                    map[cell.index] = cell.valueBefore;
                }
            }
        } else {
            // for (int i = 0; i < 1; i++) {
            for (int i = oldVal; i < newVal; i++) {
                log.debug("恢复,i={}", i);
                StepRecord record = lstStep.get(i);
                for (CellInfo cell : record.getCells()) {
                    map[cell.index] = cell.valueAfter;
                }
            }
        }
        mapLabel.notifyDataUpdated();
    }

    /**
     *
     */
    public void addRecord(StepRecord stepRecord) {
        if (cur == max) {
            log.debug("正常移动");
        } else {
            log.debug("撤消后移动, cur={}", cur);
            //删除已撤销的记录
            int size = lstStep.size();
            for (int i = size - 1; i >= cur; i--) {
                lstStep.remove(i);
            }
        }
        lstStep.add(stepRecord);
        cur = max = lstStep.size();

        notUpdateView = true;
        slider.setMaximum(max);
        slider.setValue(cur);
        notUpdateView = false;

        labStep.setText("[" + cur + "/" + max + "]");
        //不用updateView
    }

    public void reset() {
        log.debug("");
        lstStep.clear();
        cur = 0;
        max = 0;
        notUpdateView = true;
        slider.setMaximum(0);
        slider.setValue(0);
        notUpdateView = false;

        labStep.setText("[" + cur + "/" + max + "]");
        //不用updateView
    }

    public boolean undo() {
        if (cur == 0) return false;
        log.debug("undo前,cur=[{}/{}]", cur, max);
        slider.setValue(cur - 1);
        return true;
    }

    /**
     * @return true-恢复成功;false-无操作,已经是最后一步
     */
    public boolean redo() {
        if (cur == max) return false;
        log.debug("redo前,cur=[{}/{}]", cur, max);
        slider.setValue(cur + 1);
        return true;
    }

    public boolean hasRecord() {
        return !lstStep.isEmpty();
    }

    public boolean isSameWithStep(ArrayList<StepRecord> lstProgressRecord) {
        return lstStep.equals(lstProgressRecord);
    }

    /**
     * 复制操作记录
     * @param onlyBeforeCur true-仅复制[0-cur)的记录,阿狸当前位置及之前的记录;false-复制全部记录
     */
    public ArrayList<StepRecord> getStepClone(boolean onlyBeforeCur) {
        if (onlyBeforeCur) {
            ArrayList<StepRecord> res = new ArrayList<>(cur);
            for (int i = 0; i < cur; i++) res.add(lstStep.get(i));
            return res;
        } else {
            return new ArrayList<>(lstStep);
        }
    }

    /**
     * 加载记录,并定位到第0步;不触发更新回调
     */
    public void loadStep(ArrayList<StepRecord> toAdd) {
        lstStep.clear();
        lstStep.addAll(toAdd);
        cur = 0;
        max = toAdd.size();

        notUpdateView = true;
        slider.setMaximum(max);
        slider.setValue(cur);
        notUpdateView = false;

        labStep.setText("[" + cur + "/" + max + "]");
    }

    /**
     * 定位到最后一步,触发更新回调
     */
    public void toLastStep() {
        slider.setValue(max);
    }

    @Override
    public void onAutoplayEnabled(boolean enabled) {
        cbAutoplay.setSelected(false);//切换后,都不选中
        cbAutoplay.setVisible(enabled);
    }

    @Override
    public void onAutoplaySwitchedState(boolean start) {
        cbAutoplay.setSelected(start);
        //允许中途开始播放,开始时不用重置到第0步
        if (start) {
            if (cur == max) {
                log.info("从最后一步重置到0");
                slider.setValue(0);
            }
        }
    }

}
